package com.moonciki.multids.config;

import com.moonciki.multids.constant.MultiDsConstant;
import com.moonciki.multids.multi.DynamicDataSource;
import com.moonciki.multids.multi.DynamicDataSourceContextHolder;
import com.moonciki.multids.multi.FakeDataSource;
import com.moonciki.multids.properties.DataSourceProperty;
import com.moonciki.multids.properties.MultiDataSourceProperty;
import com.moonciki.multids.util.MultiSpringBeanUtil;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;
import javax.sql.XADataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * 多数据源配置
 */
@Slf4j
@AutoConfigureBefore(value = DataSourceAutoConfiguration.class)
@EnableConfigurationProperties(MultiDataSourceProperty.class)
@Configuration
@Import(value = {MultiSpringBeanUtil.class})
public class MultiDataSourceConfig {

    @Autowired
    private MultiDataSourceProperty multiDataSourceProperty;

    @Autowired
    MultiSpringBeanUtil multiSpringBeanUtil;

    public Map<String, Object> createDataSourcePropMap(DataSourceProperty dataSourceProperty) {
        Map<String, Object> propMap = new HashMap<>();

        propMap.put("jdbcUrl", dataSourceProperty.getJdbcUrl());
        propMap.put("maximumPoolSize", dataSourceProperty.getMaximumPoolSize());
        propMap.put("minimumIdle", dataSourceProperty.getMinimumIdle());

        propMap.put("username", dataSourceProperty.getUsername());
        propMap.put("password", dataSourceProperty.getPassword());

        return propMap;
    }

    private void dynamicDataSourceInit(DynamicDataSource dynamicDataSource) {

        Map<Object, Object> targetMap = new HashMap<>();

        Map<String, DataSourceProperty> dataSourceMap = multiDataSourceProperty.getDatasources();

        if(MapUtils.isNotEmpty(dataSourceMap)){
            for(Map.Entry<String, DataSourceProperty> dataSourceEntry : dataSourceMap.entrySet()){
                String dbName = dataSourceEntry.getKey();
                DataSourceProperty dataSourceProperty = dataSourceEntry.getValue();

                String beanName = DynamicDataSourceContextHolder.getDataSourceKey(dbName);

                Map<String, Object> propMap = createDataSourcePropMap(dataSourceProperty);

                MultiSpringBeanUtil.addBean(HikariDataSource.class, beanName, propMap, "close");

                HikariDataSource dataSource = MultiSpringBeanUtil.getBean(beanName, HikariDataSource.class);

                targetMap.put(beanName, dataSource);
                DynamicDataSourceContextHolder.dataSourceSet.add(beanName);

                log.info("    --- Regist_datasource : {} ", beanName);

            }
        }

        dynamicDataSource.setTargetDataSources(targetMap);
        //一定要加，否则无法获取connection
        dynamicDataSource.afterPropertiesSet();

        log.info("DBSource init successed ...");

    }


    /**
     * 默认数据源，防止未设置默认数据源
     * @return
     */
    //@Bean(name = "defaultFakeDataSource")
    public FakeDataSource defaultFakeDataSource() {
        //虚假的数据源，以便没有设置时报错
        String beanName = MultiDsConstant.default_fake_datasource;

        Map<String, Object> propMap = new HashMap<>();

        MultiSpringBeanUtil.addBean(FakeDataSource.class, beanName, propMap, "close");

        FakeDataSource fakeDataSource = MultiSpringBeanUtil.getBean(beanName, FakeDataSource.class);

        return fakeDataSource;
    }

    @Bean
    @Primary
    @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    public DataSource dynamicDataSource(){

        DynamicDataSourceContextHolder.setNamePrefix(multiDataSourceProperty.getNamePrefix());

        DataSource defaultDataSource = defaultFakeDataSource();

        Map<Object, Object> targetMap = new HashMap<>();

        DynamicDataSource dynamicDataSource = new DynamicDataSource();

        dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
        dynamicDataSource.setTargetDataSources(targetMap);

        this.dynamicDataSourceInit(dynamicDataSource);

        return dynamicDataSource;
    }

}

